/*
 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package java.lang.ref;

import java.util.function.Consumer;

/**
 * 引用队列，垃圾收集器在检测到适当的可达性变化后，
 * 将已注册的引用对象追加到此队列中。
 *
 * @author   Mark Reinhold
 * @since    1.2
 */

public class ReferenceQueue<T> {

    /**
     * 构造一个新的引用对象队列。
     */
    public ReferenceQueue() { }

    private static class Null<S> extends ReferenceQueue<S> {
        boolean enqueue(Reference<? extends S> r) {
            return false;
        }
    }

    static ReferenceQueue<Object> NULL = new Null<>();
    static ReferenceQueue<Object> ENQUEUED = new Null<>();

    static private class Lock { };
    private Lock lock = new Lock();
    private volatile Reference<? extends T> head = null;
    private long queueLength = 0;

    boolean enqueue(Reference<? extends T> r) { /* 仅由Reference类调用 */
        synchronized (lock) {
            // 检查自获得锁以来此引用是否已经被
            // 加入队列（甚至已被移除）
            ReferenceQueue<?> queue = r.queue;
            if ((queue == NULL) || (queue == ENQUEUED)) {
                return false;
            }
            assert queue == this;
            r.next = (head == null) ? r : head;
            head = r;
            queueLength++;
            // 在添加到列表*之后*更新r.queue，以避免与
            // 并发的已入队检查和快速路径poll()产生竞争。
            // volatile确保顺序。
            r.queue = ENQUEUED;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(1);
            }
            lock.notifyAll();
            return true;
        }
    }

    private Reference<? extends T> reallyPoll() {       /* 必须持有锁 */
        Reference<? extends T> r = head;
        if (r != null) {
            r.queue = NULL;
            // 在从列表中移除*之前*更新r.queue，以避免与
            // 并发的已入队检查和快速路径poll()产生竞争。
            // volatile确保顺序。
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;
            head = (rn == r) ? null : rn;
            r.next = r;
            queueLength--;
            if (r instanceof FinalReference) {
                sun.misc.VM.addFinalRefCount(-1);
            }
            return r;
        }
        return null;
    }

    /**
     * 轮询此队列以查看是否有引用对象可用。如果有一个对象可用且无需进一步延迟，
     * 则将其从队列中移除并返回。否则此方法立即返回<tt>null</tt>。
     *
     * @return  引用对象，如果有一个立即可用，否则返回<code>null</code>
     */
    public Reference<? extends T> poll() {
        if (head == null)
            return null;
        synchronized (lock) {
            return reallyPoll();
        }
    }

    /**
     * 移除此队列中的下一个引用对象，阻塞直到有一个可用或给定的超时期限到期。
     *
     * <p> 此方法不提供实时保证：它调度超时就像调用{@link Object#wait(long)}方法一样。
     *
     * @param  timeout  如果为正数，在等待引用添加到此队列时最多阻塞<code>timeout</code>毫秒。
     *                  如果为零，则无限期阻塞。
     *
     * @return  引用对象，如果在指定的超时期限内有一个可用，否则返回<code>null</code>
     *
     * @throws  IllegalArgumentException
     *          如果timeout参数的值为负数
     *
     * @throws  InterruptedException
     *          如果超时等待被中断
     */
    public Reference<? extends T> remove(long timeout)
        throws IllegalArgumentException, InterruptedException
    {
        if (timeout < 0) {
            throw new IllegalArgumentException("Negative timeout value");
        }
        synchronized (lock) {
            Reference<? extends T> r = reallyPoll();
            if (r != null) return r;
            long start = (timeout == 0) ? 0 : System.nanoTime();
            for (;;) {
                lock.wait(timeout);
                r = reallyPoll();
                if (r != null) return r;
                if (timeout != 0) {
                    long end = System.nanoTime();
                    timeout -= (end - start) / 1000_000;
                    if (timeout <= 0) return null;
                    start = end;
                }
            }
        }
    }

    /**
     * 移除此队列中的下一个引用对象，阻塞直到有一个可用。
     *
     * @return 引用对象，阻塞直到有一个可用
     * @throws  InterruptedException  如果等待被中断
     */
    public Reference<? extends T> remove() throws InterruptedException {
        return remove(0);
    }

    /**
     * 遍历队列并对每个引用调用给定的操作。
     * 适用于诊断目的。
     * 警告：此方法的任何使用都应确保不要
     * 保留被遍历引用的引用对象（在FinalReference的情况下），
     * 以免它们的生命周期被不必要地延长。
     */
    void forEach(Consumer<? super Reference<? extends T>> action) {
        for (Reference<? extends T> r = head; r != null;) {
            action.accept(r);
            @SuppressWarnings("unchecked")
            Reference<? extends T> rn = r.next;
            if (rn == r) {
                if (r.queue == ENQUEUED) {
                    // 仍在队列中 -> 我们到达了链的末尾
                    r = null;
                } else {
                    // 已出队：r.queue == NULL; ->
                    // 当被队列轮询器超越时从头重新开始
                    r = head;
                }
            } else {
                // 链中的下一个
                r = rn;
            }
        }
    }
}
